#include "tumors.h"

static void copyFromFile(FILE *, list *);
static void calcTurmors(void *);
static float checkDistance(float, float);
static void waitRepeated(void *);
static short int createThreads(pthread_t *, pthread_t *, sem_t *, 
                        sem_t *, hashTable **, parThreads *);

static list buffer;
static hashTable hashRepeated;

int tumors(FILE *file)
{
    pthread_t threads[NUMBER_THREADS];    
    //cellHash *showCell;
    unsigned int i = 0;
    unsigned int p[NUMBER_THREADS];

    if ( !initList(&buffer, sizeof(element)) || !initHash(&hashRepeated))
        return 0;

    copyFromFile(file, &buffer);

    for (i = 0; i < NUMBER_THREADS && i < buffer.size; i++){
        p[i] = i;
        if (pthread_create(&threads[i], NULL, (void *)&calcTurmors, (void *)&p[i])){
            printf("Erro, i could not create the thread %u\n", i);
            return 0;
        }
    }
    
    for (i = 0; i < NUMBER_THREADS && i < buffer.size; i++)
        pthread_join(threads[i], NULL);
    
    showOrderedHash(&hashRepeated);

    endList(&buffer);
    pthread_exit(NULL);
    
    return 1;
}

static void waitRepeated(void *p)
{
    parThreads *f = (parThreads *)p;
    unsigned int i, c = f->sPosition, st = 1;
    float temp;
    sem_t mutex;
    
    if (sem_init(&mutex, 0, 1) == -1)
        return;
    
    while (st){
        sem_wait(&f->semSon);
        sem_wait(&mutex);
        
        for (i = 0; i < f->pLists[c]->size; i++){
            temp = *(float *)getElementList(f->pLists[c], i);
            insertHash(&hashRepeated, &temp, 1);
        }

        cleanList(f->pLists[c]);
        
        sem_wait(&f->mutexsBuffer);
        f->nBuffer--;
        if (f->nBuffer < 0){
            st = !st;
            sem_post(&f->exit);
        }
        sem_post(&f->mutexsBuffer);
        
        sem_post(&f->semFather);
        c = (c+NUMBER_CONSUMERS)%NUMBER_BUFFERS;
        
        sem_post(&mutex);
    }
    
    pthread_exit(NULL);
}

static void calcTurmors(void *data)
{
    unsigned int i = 0, j = 0, cBuffer = 0;
    list bufferList[NUMBER_BUFFERS];
    unsigned char cConsumers = 0, savecConsumers = 0;
    float r;
    
    pthread_t son[NUMBER_CONSUMERS];
    parThreads pWait[NUMBER_CONSUMERS];
    
    if (NUMBER_BUFFERS <= 1){
        fprintf(stdout, "Erro, O número de buffer deve ser maior do que 1\n");
        return ;
    }
    
    for (i = 0; i < NUMBER_BUFFERS; i++){
        if ( !initList(&bufferList[i], sizeof(float)))
            return ;

        for (j = 0; j < NUMBER_CONSUMERS; j++)
            pWait[j].pLists[i] = &bufferList[i];
    }

    for (i = 0; i < NUMBER_CONSUMERS; i++){
        pWait[i].nBuffer = 0;
        pWait[i].sPosition = i;

        if (sem_init(&pWait[i].semSon, 0, 0) == -1 || sem_init(&pWait[i].exit, 0, 0) == -1 ||
                sem_init(&pWait[i].semFather, 0, SIZE_SEGMENT-1) == -1 || sem_init(&pWait[i].mutexsBuffer, 0, 1) == -1)
            return;
    }

    i = 0;
    do {
        if (pthread_create(&son[i], NULL, (void *)&waitRepeated, (void *)&pWait[i]))
            return ;
        i++;
    } while (i < NUMBER_CONSUMERS);
    
    for (i = *(unsigned int *)data; i < buffer.size; i += NUMBER_THREADS){
        for (j = i+1; j < buffer.size; j++){
            if (checkStatusList(&bufferList[cBuffer])){
                sem_wait(&pWait[cConsumers].semFather);
                
                sem_wait(&pWait[cConsumers].mutexsBuffer);
                pWait[cConsumers].nBuffer++;
                sem_post(&pWait[cConsumers].mutexsBuffer);
                
                sem_post(&pWait[cConsumers].semSon);
                cConsumers = (++cConsumers)%NUMBER_CONSUMERS;
                cBuffer = (++cBuffer)%NUMBER_BUFFERS;
            }
            
            r = checkDistance(i, j);
            insertList(&bufferList[cBuffer], (void *)&r);
        }
    }
    
    savecConsumers = cConsumers;
    
    do {
        sem_post(&pWait[savecConsumers].semSon);
        sem_wait(&pWait[savecConsumers].exit);
        savecConsumers = (++savecConsumers)%NUMBER_CONSUMERS;
    } while (savecConsumers != cConsumers);

    pthread_exit(NULL);
}

static float checkDistance(float j, float k)
{
    float x, y, z;
    
    x = (float)(((element *)getElementList(&buffer, j))->x - ((element *)getElementList(&buffer, k))->x);
    y = (float)(((element *)getElementList(&buffer, j))->y - ((element *)getElementList(&buffer, k))->y);
    z = (float)(((element *)getElementList(&buffer, j))->z - ((element *)getElementList(&buffer, k))->z);

    return (float)sqrt((float)(x*x+y*y+z*z));
}

static void copyFromFile(FILE *file, list *l)
{
    float x, y, z;
    element temp;

    while (fscanf(file, "%f %f %f", &x, &y, &z) != -1){
        temp.x = x;
        temp.y = y;
        temp.z = z;
        insertList(l, (void *)&temp);
    }
}
